package com.wei.czz.common.aspect;

import com.wei.czz.common.annotation.Auth;
import com.wei.czz.common.annotation.AuthLogin;
import com.wei.czz.common.annotation.AuthPermit;
import com.wei.czz.common.annotation.AuthRole;
import com.wei.czz.common.enums.ResultEnum;
import com.wei.czz.common.exception.CzzException;
import com.wei.czz.common.utils.SecurityUtils;
import com.wei.czz.framework.security.entity.SecurityUser;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2024-11-27 10:24:13
 * className: AuthAspect 权限校验切面类
 * version: 1.0
 * description:
 */
@Order(2)
@Aspect
@Component
@AllArgsConstructor
public class AuthAspect {

    private static final Logger log = LoggerFactory.getLogger(AuthAspect.class);

    /**
     * 所有使用 Auth 注解的方法作为切点
     */
    @Pointcut("@annotation(com.wei.czz.common.annotation.Auth)")
    public void auth() {}

    /**
     * 所有使用 AuthLogin 注解的方法作为切点
     */
    @Pointcut("@annotation(com.wei.czz.common.annotation.AuthLogin)")
    public void authLogin() {}

    /**
     * 所有使用 AuthPermit 注解的方法作为切点
     */
    @Pointcut("@annotation(com.wei.czz.common.annotation.AuthPermit)")
    public void authPermit() {}

    /**
     * 所有使用 AuthRole 注解的方法作为切点
     */
    @Pointcut("@annotation(com.wei.czz.common.annotation.AuthRole)")
    public void authRole() {}

    /**
     * 前置通知
     * `@Before`：本方法会在目标方法执行之前执行
     */
    @Before("auth() || authLogin() || authPermit() || authRole()")
    public void checkResubmit(JoinPoint point) {

        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        // 获取权限校验注解
        Annotation annotation = this.getAnnotation(methodSignature);
        if (Objects.isNull(annotation)) {
            log.warn("注解对象解析结果为空。className={} methodName={}", methodSignature.getDeclaringType().getSimpleName(),
                    methodSignature.getName());
            return;
        }

        // 获取请求登录用户
        Optional<SecurityUser> optional = SecurityUtils.securityUserOptional();
        if (optional.isEmpty()) {
            throw new CzzException(ResultEnum.TOKEN_EXPIRE.getCode(), "登录已过期，请重新登陆后再操作");
        }
        if (optional.get().getSuperManager()) {
            log.info("超级管理员，无需进行权限校验");
            return;
        }

        String errorMsg = StringUtils.EMPTY;
        if (annotation instanceof Auth) {
            Auth auth = (Auth) annotation;

            String[] permit = auth.permit();
            String[] roles = auth.roles();
            if (permit.length == 0 && roles.length == 0) {
                log.warn("权限注解未标明权限标识和角色校验范围。className={} methodName={}",
                        methodSignature.getDeclaringType().getSimpleName(), methodSignature.getName());
            }
            // 校验权限标识
            String permitMsg = this.validPermit(permit);
            // 校验角色
            String roleMsg = this.validRole(roles);
            // 错误提示合并
            if (!StringUtils.isAllBlank(permitMsg, roleMsg)) {
                if (Objects.equals(permitMsg, roleMsg)) {
                    errorMsg = permitMsg;
                } else {
                    errorMsg = permitMsg + ";" + roleMsg;
                }
            } else if (StringUtils.isNotBlank(permitMsg)) {
                errorMsg = permitMsg;
            } else if (StringUtils.isNotBlank(roleMsg)) {
                errorMsg = roleMsg;
            }

        } else if (annotation instanceof AuthLogin) {
            // empty

        } else if (annotation instanceof AuthRole) {
            AuthRole authRole = (AuthRole) annotation;

            String[] roles = authRole.value();
            if (roles.length == 0) {
                log.warn("权限注解未标明角色校验范围。className={} methodName={}",
                        methodSignature.getDeclaringType().getSimpleName(), methodSignature.getName());
            }
            // 校验角色
            errorMsg = this.validRole(roles);

        } else if (annotation instanceof AuthPermit) {
            AuthPermit authPermit = (AuthPermit) annotation;

            String[] permits = authPermit.value();
            if (permits.length == 0) {
                log.warn("权限注解未标明权限标识校验范围。className={} methodName={}",
                        methodSignature.getDeclaringType().getSimpleName(), methodSignature.getName());
            }
            // 校验权限标识
            errorMsg = this.validPermit(permits);

        } else {
            log.warn("未实现权限校验注解的校验逻辑。annotation={}", annotation);
        }

        if (StringUtils.isNotBlank(errorMsg)) {
            throw new CzzException(ResultEnum.AUTH_FAIL.getCode(), errorMsg);
        }
    }

    /**
     * 获取要进行校验的注解
     * 同时获取类和方法上的注解，方法注解的优先级比类注解高，同一级别则根据注解标记的优先级字段来决定使用哪个，同一级别的注解优先级字段一致，则按照顺序使用后面的注解
     * @param methodSignature 方法签名对象
     * @return 注解对象
     */
    private Annotation getAnnotation(MethodSignature methodSignature) {
        Annotation[] classAnnotations = methodSignature.getDeclaringType().getAnnotations();
//        log.debug("类注解：个数={} {}", classAnnotations.length, classAnnotations);
        Annotation[] methodAnnotations = methodSignature.getMethod().getAnnotations();
//        log.debug("方法注解：个数={} {}", methodAnnotations.length, methodAnnotations);
        // 获取类上使用的权限注解
        Annotation classAnnotation = this.getAnnotation(classAnnotations);
        // 获取方法上使用的权限注解
        Annotation methodAnnotation = this.getAnnotation(methodAnnotations);
        // 方法注解优先级比类注解高
        return Objects.nonNull(methodAnnotation) ? methodAnnotation : classAnnotation;
    }

    /**
     * 获取权限校验注解
     * @param annotations 注解对象数组
     * @return 注解对象
     */
    private Annotation getAnnotation(Annotation[] annotations) {
        Integer order = null;
        Annotation authAnnotation = null;
        // 遍历注解
        for (Annotation annotation : annotations) {
            Integer annotationOrder = null;

            if (annotation instanceof Auth) {
                Auth auth = (Auth) annotation;

                annotationOrder = auth.order();
            } else if (annotation instanceof AuthLogin) {
                AuthLogin authLogin = (AuthLogin) annotation;

                annotationOrder = authLogin.order();
            } else if (annotation instanceof AuthPermit) {
                AuthPermit authPermit = (AuthPermit) annotation;

                annotationOrder = authPermit.order();
            } else if (annotation instanceof AuthRole) {
                AuthRole authRole = (AuthRole) annotation;

                annotationOrder = authRole.order();
            } else {
                // empty
            }
            if (Objects.isNull(annotationOrder)) {
                // 不是权限校验注解
                continue;
            }

            if (Objects.isNull(authAnnotation)) {
                order = annotationOrder;
                authAnnotation = annotation;
            } else if (order.compareTo(annotationOrder) >= 0) {
                order = annotationOrder;
                authAnnotation = annotation;
            }
        }
        return authAnnotation;
    }

    /**
     * 校验权限标识，用户包含任意权限标识即可
     * @param permits 权限标识数组
     * @return 错误提示
     */
    private String validPermit(String[] permits) {
        if (permits.length == 0) {
            return StringUtils.EMPTY;
        }

        SecurityUser securityUser = SecurityUtils.getSecurityUser();

        Set<String> permitSet = securityUser.getPermitSet();
        for (String permit : permits) {
            if (permitSet.contains(permit)) {
                return StringUtils.EMPTY;
            }
        }
        return "‘" + securityUser.getUsername() + "’用户未拥有以下权限标识：" + String.join(",", permits);
    }

    /**
     * 校验角色，用户包含任意角色即可
     * @param roles  角色键数组
     * @return 错误提示
     */
    private String validRole(String[] roles) {
        if (roles.length == 0) {
            return StringUtils.EMPTY;
        }

        SecurityUser securityUser = SecurityUtils.getSecurityUser();

        Set<String> roleSet = securityUser.getRoleKeySet();
        for (String role : roles) {
            if (roleSet.contains(role)) {
                return StringUtils.EMPTY;
            }
        }
        return "‘" + securityUser.getUsername() + "’用户未拥有以下角色：" + String.join(",", roles);
    }
}
